Introduction

Resolving geographic units that do not neatly coincide is a common problem in spatial data analysis. This method attempts to conflate King County Health Reporting Areas (HRAs) to US Census tracts. In the cases where a given tract with entirely within a HRA, that tract receives the HRA’s ID. Where a given tract overlaps multiple HRAs block-level census data is used to determine which HRA ID to assign to the tract.

Census Block Counts

This method provides three alternatives of block-level counts that can be used:

if(!file.exists(root_file('1-data/4-interim/kc-blk-sp.gpkg'))){
        pop <- read_csv(root_file('1-data/3-external/manual/wa-blk/DEC_10_SF1_P1/DEC_10_SF1_P1_with_ann.csv'), 
                        col_types = cols(Id2 = col_character()), 
                        skip = 1) %>% 
                mutate(GEO_ID_BLK = Id2,
                       GEOID_TR = substr(Id2,start = 1,stop = 11),
                       POP = Total) %>% 
                select(GEO_ID_BLK,GEOID_TR,POP)
        
        hu <- read_csv(root_file('1-data/3-external/manual/wa-blk/DEC_10_SF1_H1/DEC_10_SF1_H1_with_ann.csv'), 
                       col_types = cols(Id2 = col_character()), 
                       skip = 1) %>% 
                mutate(GEO_ID_BLK = Id2,
                       HU = Total) %>% 
                select(GEO_ID_BLK,HU)
        
        pophu <- read_csv(root_file('1-data/3-external/manual/wa-blk/DEC_10_SF1_H10/DEC_10_SF1_H10_with_ann.csv'), 
                          col_types = cols(Id2 = col_character()), 
                          skip = 1) %>% 
                mutate(GEO_ID_BLK = Id2,
                       POPHU = Total) %>% 
                select(GEO_ID_BLK,POPHU)
        
        cnts <- left_join(pop,hu,by = 'GEO_ID_BLK') %>% 
                left_join(pophu,by = 'GEO_ID_BLK')
        
        # Source for KC Blocks spatial data
        if(!file.exists(root_file('1-data/3-external/wa-blk/blocks10/blocks10.shp'))){
                url <- 'ftp://ftp.kingcounty.gov/gis-web/web/GISData/blocks10_SHP.zip' # direct URL to the file download
                
                temp <- tempfile() # create a temporary file to hold the compressed download
                
                download(url, dest = temp, mode='wb') # download the file
                
                unzip (temp, exdir = root_file('1-data/3-external/wa-blk')) # extract the file to the project folder
        }
        
        kc_blk <- readOGR(dsn = root_file('1-data/3-external/wa-blk/blocks10/'),
                          layer = 'blocks10',
                          verbose = FALSE,
                          stringsAsFactors = FALSE)
        
        kc_blk@data %<>% left_join(cnts, by = 'GEO_ID_BLK') %>% 
                select(GEOID_BLK = GEO_ID_BLK,
                       GEOID_TR:POPHU)
        
        kc_blk %>% writeOGR(dsn = root_file('1-data/4-interim/kc-blk-sp.gpkg'),
                            layer = 'kc_blk_sp',
                            driver = 'GPKG',
                            overwrite_layer = TRUE,verbose = FALSE)
}
kc_blk_sp <- readOGR(dsn = root_file('1-data/4-interim/kc-blk-sp.gpkg'),
                            layer = 'kc_blk_sp',
                  stringsAsFactors = FALSE,
                  verbose = FALSE)

The Algorithm

The following actions are performed in this method:

  1. Centroids of the census block polygons are calculated (class = SpatialPointsDataFrame)
  2. HRA IDs are passed to the block centroid using a spatial overlay method (sp::over())
  3. Blocks are aggregated into tracts and the count variables (POP,HU,POPHU) are summed
  4. For each count variable, the HRA ID with the highest sum is assigned to each tract
# Pass HRA IDs to the block centroids
hra <- readOGR(dsn = root_file('1-data/3-external/manual/HRA_2010Block_Clip/'),layer = 'HRA_2010Block_Clip',
               verbose = FALSE,stringsAsFactors = FALSE)
kc_blk_cnt_sp <- SpatialPointsDataFrame(coords = rgeos::gCentroid(kc_blk_sp,byid = TRUE),
                               data = as.data.frame(kc_blk_sp@data),
                               match.ID = FALSE)
kc_blk_cnt_sp$HRA_ID <- sp::over(kc_blk_cnt_sp,hra[,'HRA2010v2_']) %>% unlist
kc_blk_cnt_sp@data %<>% mutate(HRA_ID = ifelse(is.na(HRA_ID),'None',HRA_ID))
# Assign HRAs to tracts
 
if(!file.exists(root_file('1-data/4-interim/kc-hra-tr-sp.gpkg'))){
        if(!file.exists(root_file('1-data/3-external/kc-tr/tracts10_shore/tracts10_shore.shp'))){
                
                url <- 'ftp://ftp.kingcounty.gov/gis-web/web/GISData/tracts10_shore_SHP.zip' # direct URL to the file download
                
                temp <- tempfile() # create a temporary file to hold the compressed download
                
                download(url, dest = temp, mode='wb') # download the file
                
                unzip (temp, exdir = root_file('1-data/3-external/kc-tr')) # extract the file to the project folder
                
                
        }
         
        kc_tr_sp <- readOGR(dsn = root_file('1-data/3-external/kc-tr/tracts10_shore/'),
                            layer = 'tracts10_shore',
                            verbose = FALSE,
                            stringsAsFactors = FALSE)
        kc_tr_sp@data %<>% select(GEOID_TR = GEO_ID_TRT)
        
        first_notNA <- function(x){first(x[!is.na(x)])}
        
        tr_hra_ids <- 
                kc_blk_cnt_sp@data %>% 
                as.data.frame() %>% 
                gather('VAR','COUNT',POP:POPHU) %>% 
                group_by(GEOID_TR,VAR,HRA_ID) %>% 
                summarise(SUM = sum(COUNT)) %>% 
                arrange(desc(SUM)) %>% 
                slice(1) %>% 
                spread(VAR,HRA_ID) %>% 
                mutate(HU_CNT = ifelse(!is.na(HU),SUM,NA_integer_),
                       POP_CNT = ifelse(!is.na(POP),SUM,NA_integer_),
                       POPHU_CNT = ifelse(!is.na(POPHU),SUM,NA_integer_)) %>% 
                select(-SUM) %>% 
                group_by(GEOID_TR) %>% 
                summarise_all(funs(first_notNA)) %>% 
                ungroup() %>% 
                select(GEOID_TR,
                       POP = POP_CNT,
                       HU = HU_CNT,
                       POPHU = POPHU_CNT,
                       HRA_POP = POP,
                       HRA_HU = HU,
                       HRA_POPHU = POPHU)
        
        tr_hra_ids2 <- 
                tr_hra_ids %>% 
                select(GEOID_TR,matches('HRA')) %>% 
                gather("VAR","HRA",matches('HRA')) %>%  
                group_by(GEOID_TR) %>% 
                summarise(ALLEQ = length(unique(HRA))==1) %>%
                left_join(tr_hra_ids,.,by = 'GEOID_TR') 
        
        # Get the percentage for each count variable
        
        pop_tr <- 
                read_csv(root_file("1-data/3-external/manual/kc-tr/DEC_10_SF1_P1/DEC_10_SF1_P1_with_ann.csv"), 
                         col_types = cols(GEO.id2 = col_character(), 
                                          Geography = col_skip(), 
                                          Id = col_skip(), 
                                          Id2 = col_character()), 
                         skip = 1) %>% 
                select(GEOID_TR = Id2,
                       POP_TR = Total)
        
        hu_tr <- 
                read_csv(root_file('1-data/3-external/manual/kc-tr/DEC_10_SF1_H1/DEC_10_SF1_H1_with_ann.csv'), 
                         col_types = cols(GEO.id2 = col_character(), 
                                          Geography = col_skip(), 
                                          Id = col_skip(), 
                                          Id2 = col_character()), 
                         skip = 1)%>% 
                select(GEOID_TR = Id2,
                       HU_TR = Total)
        
        pophu_tr <- 
                read_csv(root_file('1-data/3-external/manual/kc-tr/DEC_10_SF1_H10/DEC_10_SF1_H10_with_ann.csv'), 
                         col_types = cols(GEO.id2 = col_character(), 
                                          Geography = col_skip(), 
                                          Id = col_skip(), 
                                          Id2 = col_character()), 
                         skip = 1) %>% 
                select(GEOID_TR = Id2,
                       POPHU_TR = Total)
        
        kc_tr_sp@data %<>%
                left_join(.,tr_hra_ids2,by = 'GEOID_TR') %>% 
                left_join(.,pop_tr,by = 'GEOID_TR') %>% 
                left_join(.,hu_tr,by = 'GEOID_TR') %>% 
                left_join(.,pophu_tr,by = 'GEOID_TR') %>% 
                mutate(POP_PCT = round_any(POP/POP_TR,accuracy = .01),
                       HU_PCT = round_any(HU/HU_TR,accuracy = .01),
                       POPHU_PCT = round_any(POPHU/POPHU_TR,accuracy = .01)) %>% 
                select(GEOID_TR,
                       POP,POP_PCT,
                       HU, HU_PCT,
                       POPHU, POPHU_PCT,everything())
        
        kc_tr_sp %>% 
                writeOGR(dsn = root_file('1-data/4-interim/kc-hra-tr-sp.gpkg'),
                         layer = 'kc_hra_tr_sp',
                         driver = 'GPKG',
                         verbose = FALSE,
                         overwrite_layer = TRUE)
        
}
kc_hra_tr_sp <- 
        readOGR(dsn = root_file('1-data/4-interim/kc-hra-tr-sp.gpkg'),
                layer = 'kc_hra_tr_sp',
                verbose = FALSE) %>% 
        spTransform(crs_proj)

After running the assignment algorithm, it is clear that the POP and POPHU variables result in the same HRA assignments. HU differs from the other two variables in three of the tracts:

GEOID_TR HRA_POP HRA_POPHU HRA_HU
53033022202 Kirkland North Kirkland North Kirkland
53033025001 Bellevue-South Bellevue-South Newcastle/Four Creeks
53033028801 SeaTac/Tukwila SeaTac/Tukwila Des Moines/Normandy Park

Maps

mypal <- RColorBrewer::brewer.pal(8,name = 'Set2')[-8]
shuffled_hra_pop <- forcats::fct_shuffle(kc_hra_tr_sp$HRA_POP) %>% factor(ordered = T)
shuffled_hra_hu <- forcats::fct_shuffle(kc_hra_tr_sp$HRA_HU) %>% factor(ordered = T)
shuffled_hra_pophu <- forcats::fct_shuffle(kc_hra_tr_sp$HRA_POPHU) %>% factor(ordered = T)
pal_pop <- colorFactor(palette = mypal,domain = shuffled_hra_pop)
pal_hu <- colorFactor(palette = mypal,domain = shuffled_hra_hu)
pal_pophu <- colorFactor(palette = mypal,domain = shuffled_hra_pophu)
hra %<>% spTransform(crs_proj)
show_hra_tr_pop <- function(){
        myLfltGrey(bumpLabels = FALSE,hideControls = FALSE) %>%
        addProviderTiles(providers$CartoDB) %>% 
        addPolygons(data = kc_hra_tr_sp,
                    smoothFactor = 0,
                    weight = 1,
                    color = col2hex("white"),
                    opacity = .85,
                    fillColor = ~pal_pop(shuffled_hra_pop),
                    fillOpacity = .5,
                    group = 'HRA Tract by Pop.',
                    popup = ~paste0(kc_hra_tr_sp$GEOID_TR)) %>% 
        addPolygons(data = hra,
                    smoothFactor = 0,
                    fillOpacity = 0,
                    weight = 2,
                    color = ~pal_pop(factor(hra$HRA2010v2_,levels = levels(shuffled_hra_pop),ordered = TRUE)),
                    opacity = 1,
                    group = 'HRAs',
                    popup = ~paste0(hra$HRA2010v2_)) %>% 
        addLayersControl(overlayGroups = c('HRA Tract by Pop.',
                                           'HRAs'),
                         position = 'topright',options = layersControlOptions(FALSE))
}
show_hra_tr_hu <- function(){
        myLfltGrey(bumpLabels = FALSE,hideControls = FALSE) %>%
        addProviderTiles(providers$CartoDB) %>% 
        addPolygons(data = kc_hra_tr_sp,
                    smoothFactor = 0,
                    weight = 1,
                    color = col2hex("white"),
                    opacity = .85,
                    fillColor = ~pal_hu(shuffled_hra_hu),
                    fillOpacity = .5,
                    group = 'HRA Tract by Pop.',
                    popup = ~paste0(kc_hra_tr_sp$GEOID_TR)) %>% 
        addPolygons(data = hra,
                    smoothFactor = 0,
                    fillOpacity = 0,
                    weight = 2,
                    color = ~pal_hu(factor(hra$HRA2010v2_,levels = levels(shuffled_hra_hu),ordered = TRUE)),
                    opacity = 1,
                    group = 'HRAs',
                    popup = ~paste0(hra$HRA2010v2_)) %>% 
        addLayersControl(overlayGroups = c('HRA Tract by Pop.',
                                           'HRAs'),
                         position = 'topright',options = layersControlOptions(FALSE))
}
show_hra_tr_pophu <- function(){
        myLfltGrey(bumpLabels = FALSE,hideControls = FALSE) %>%
        addProviderTiles(providers$CartoDB) %>% 
        addPolygons(data = kc_hra_tr_sp,
                    smoothFactor = 0,
                    weight = 1,
                    color = col2hex("white"),
                    opacity = .85,
                    fillColor = ~pal_pophu(shuffled_hra_pophu),
                    fillOpacity = .5,
                    group = 'HRA Tract by Pop.',
                    popup = ~paste0(kc_hra_tr_sp$GEOID_TR)) %>% 
        addPolygons(data = hra,
                    smoothFactor = 0,
                    fillOpacity = 0,
                    weight = 2,
                    color = ~pal_pophu(factor(hra$HRA2010v2_,levels = levels(shuffled_hra_pophu),ordered = TRUE)),
                    opacity = 1,
                    group = 'HRAs',
                    popup = ~paste0(hra$HRA2010v2_)) %>% 
        addLayersControl(overlayGroups = c('HRA Tract by Pop.',
                                           'HRAs'),
                         position = 'topright',options = layersControlOptions(FALSE))
}
# Save the maps as HTML documents
if(!file.exists(root_file('3-communication/others/html/hra-tracts-pop.html'))){
        show_hra_tr_pop() %>% 
        saveWidget(file = root_file('3-communication/others/html/hra-tracts-pop.html'),
                           selfcontained = FALSE,
                           libdir = root_file('3-communication/others/html/html_support_files'))
}
if(!file.exists(root_file('3-communication/others/html/hra-tracts-hu.html'))){
        show_hra_tr_pop() %>% 
        saveWidget(file = root_file('3-communication/others/html/hra-tracts-hu.html'),
                           selfcontained = FALSE,
                           libdir = root_file('3-communication/others/html/html_support_files'))
}
if(!file.exists(root_file('3-communication/others/html/hra-tracts-pophu.html'))){
        show_hra_tr_pop() %>% 
        saveWidget(file = root_file('3-communication/others/html/hra-tracts-pophu.html'),
                           selfcontained = FALSE,
                           libdir = root_file('3-communication/others/html/html_support_files'))
}
show_hra_tr_pop()
show_hra_tr_hu()
show_hra_tr_pophu()
LS0tCmFsd2F5c19hbGxvd19odG1sOiB5ZXMKZGZfcHJpbnQ6IHRpYmJsZQpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogaGlkZQogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKICBwZGZfZG9jdW1lbnQ6CiAgICBrZWVwX3RleDogeWVzCi0tLQoKYGBge3IgaHJhLXRyLXNldHVwLCBlY2hvID0gRkFMU0UsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRSxjb21tZW50PUZBTFNFfQpsaWJyYXJ5KHBseXIpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkocnByb2pyb290KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShyZ2RhbCkKbGlicmFyeShzcCkKbGlicmFyeShyZ2VvcykKbGlicmFyeShtaXNjZ2lzKQpsaWJyYXJ5KHRpZ3JpcykKbGlicmFyeShsZWFmbGV0KQpsaWJyYXJ5KGdndGhlbWVzKQpsaWJyYXJ5KG1hZ3JpdHRyKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkoZG93bmxvYWRlcikKbGlicmFyeSh3ZWJzaG90KQpsaWJyYXJ5KGh0bWx0b29scykKbGlicmFyeShncGxvdHMpCmxpYnJhcnkoZ2dtYXApCmxpYnJhcnkoc2hpbnkpCmxpYnJhcnkoaHRtbHdpZGdldHMpCmxpYnJhcnkocmVhZHhsKQpsaWJyYXJ5KGFjcykKbGlicmFyeShSQ29sb3JCcmV3ZXIpCnJvb3QgPC0gcnByb2pyb290Ojppc19yc3R1ZGlvX3Byb2plY3QKcm9vdF9maWxlIDwtIHJvb3QkbWFrZV9maXhfZmlsZSgpCm9wdHNfY2h1bmskc2V0KGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGNvbW1lbnQ9RkFMU0UpCmBgYAoKIyMjIEludHJvZHVjdGlvbgpSZXNvbHZpbmcgZ2VvZ3JhcGhpYyB1bml0cyB0aGF0IGRvIG5vdCBuZWF0bHkgY29pbmNpZGUgaXMgYSBjb21tb24gcHJvYmxlbSBpbiBzcGF0aWFsIGRhdGEgYW5hbHlzaXMuIFRoaXMgbWV0aG9kIGF0dGVtcHRzIHRvIGNvbmZsYXRlIEtpbmcgQ291bnR5IEhlYWx0aCBSZXBvcnRpbmcgQXJlYXMgKEhSQXMpIHRvIFVTIENlbnN1cyB0cmFjdHMuIEluIHRoZSBjYXNlcyB3aGVyZSBhIGdpdmVuIHRyYWN0IHdpdGggZW50aXJlbHkgd2l0aGluIGEgSFJBLCB0aGF0IHRyYWN0IHJlY2VpdmVzIHRoZSBIUkEncyBJRC4gV2hlcmUgYSBnaXZlbiB0cmFjdCBvdmVybGFwcyBtdWx0aXBsZSBIUkFzIGJsb2NrLWxldmVsIGNlbnN1cyBkYXRhIGlzIHVzZWQgdG8gZGV0ZXJtaW5lIHdoaWNoIEhSQSBJRCB0byBhc3NpZ24gdG8gdGhlIHRyYWN0LiAKCgojIyMgQ2Vuc3VzIEJsb2NrIENvdW50cwpUaGlzIG1ldGhvZCBwcm92aWRlcyB0aHJlZSBhbHRlcm5hdGl2ZXMgb2YgYmxvY2stbGV2ZWwgY291bnRzIHRoYXQgY2FuIGJlIHVzZWQ6CgogICogUG9wdWxhdGlvbgogICogSG91c2luZyBVbml0cwogICogUG9wdWxhdGlvbiBpbiBIb3VzaW5nIFVuaXRzCiAgCgpgYGB7ciBocmEtdHItYmxrc30KCmlmKCFmaWxlLmV4aXN0cyhyb290X2ZpbGUoJzEtZGF0YS80LWludGVyaW0va2MtYmxrLXNwLmdwa2cnKSkpewogICAgICAgIHBvcCA8LSByZWFkX2Nzdihyb290X2ZpbGUoJzEtZGF0YS8zLWV4dGVybmFsL21hbnVhbC93YS1ibGsvREVDXzEwX1NGMV9QMS9ERUNfMTBfU0YxX1AxX3dpdGhfYW5uLmNzdicpLCAKICAgICAgICAgICAgICAgICAgICAgICAgY29sX3R5cGVzID0gY29scyhJZDIgPSBjb2xfY2hhcmFjdGVyKCkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgc2tpcCA9IDEpICU+JSAKICAgICAgICAgICAgICAgIG11dGF0ZShHRU9fSURfQkxLID0gSWQyLAogICAgICAgICAgICAgICAgICAgICAgIEdFT0lEX1RSID0gc3Vic3RyKElkMixzdGFydCA9IDEsc3RvcCA9IDExKSwKICAgICAgICAgICAgICAgICAgICAgICBQT1AgPSBUb3RhbCkgJT4lIAogICAgICAgICAgICAgICAgc2VsZWN0KEdFT19JRF9CTEssR0VPSURfVFIsUE9QKQogICAgICAgIAogICAgICAgIGh1IDwtIHJlYWRfY3N2KHJvb3RfZmlsZSgnMS1kYXRhLzMtZXh0ZXJuYWwvbWFudWFsL3dhLWJsay9ERUNfMTBfU0YxX0gxL0RFQ18xMF9TRjFfSDFfd2l0aF9hbm4uY3N2JyksIAogICAgICAgICAgICAgICAgICAgICAgIGNvbF90eXBlcyA9IGNvbHMoSWQyID0gY29sX2NoYXJhY3RlcigpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgc2tpcCA9IDEpICU+JSAKICAgICAgICAgICAgICAgIG11dGF0ZShHRU9fSURfQkxLID0gSWQyLAogICAgICAgICAgICAgICAgICAgICAgIEhVID0gVG90YWwpICU+JSAKICAgICAgICAgICAgICAgIHNlbGVjdChHRU9fSURfQkxLLEhVKQogICAgICAgIAogICAgICAgIHBvcGh1IDwtIHJlYWRfY3N2KHJvb3RfZmlsZSgnMS1kYXRhLzMtZXh0ZXJuYWwvbWFudWFsL3dhLWJsay9ERUNfMTBfU0YxX0gxMC9ERUNfMTBfU0YxX0gxMF93aXRoX2Fubi5jc3YnKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sX3R5cGVzID0gY29scyhJZDIgPSBjb2xfY2hhcmFjdGVyKCkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBza2lwID0gMSkgJT4lIAogICAgICAgICAgICAgICAgbXV0YXRlKEdFT19JRF9CTEsgPSBJZDIsCiAgICAgICAgICAgICAgICAgICAgICAgUE9QSFUgPSBUb3RhbCkgJT4lIAogICAgICAgICAgICAgICAgc2VsZWN0KEdFT19JRF9CTEssUE9QSFUpCiAgICAgICAgCiAgICAgICAgY250cyA8LSBsZWZ0X2pvaW4ocG9wLGh1LGJ5ID0gJ0dFT19JRF9CTEsnKSAlPiUgCiAgICAgICAgICAgICAgICBsZWZ0X2pvaW4ocG9waHUsYnkgPSAnR0VPX0lEX0JMSycpCiAgICAgICAgCiAgICAgICAgIyBTb3VyY2UgZm9yIEtDIEJsb2NrcyBzcGF0aWFsIGRhdGEKICAgICAgICBpZighZmlsZS5leGlzdHMocm9vdF9maWxlKCcxLWRhdGEvMy1leHRlcm5hbC93YS1ibGsvYmxvY2tzMTAvYmxvY2tzMTAuc2hwJykpKXsKICAgICAgICAgICAgICAgIHVybCA8LSAnZnRwOi8vZnRwLmtpbmdjb3VudHkuZ292L2dpcy13ZWIvd2ViL0dJU0RhdGEvYmxvY2tzMTBfU0hQLnppcCcgIyBkaXJlY3QgVVJMIHRvIHRoZSBmaWxlIGRvd25sb2FkCiAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgIHRlbXAgPC0gdGVtcGZpbGUoKSAjIGNyZWF0ZSBhIHRlbXBvcmFyeSBmaWxlIHRvIGhvbGQgdGhlIGNvbXByZXNzZWQgZG93bmxvYWQKICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgZG93bmxvYWQodXJsLCBkZXN0ID0gdGVtcCwgbW9kZT0nd2InKSAjIGRvd25sb2FkIHRoZSBmaWxlCiAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgIHVuemlwICh0ZW1wLCBleGRpciA9IHJvb3RfZmlsZSgnMS1kYXRhLzMtZXh0ZXJuYWwvd2EtYmxrJykpICMgZXh0cmFjdCB0aGUgZmlsZSB0byB0aGUgcHJvamVjdCBmb2xkZXIKICAgICAgICB9CiAgICAgICAgCiAgICAgICAga2NfYmxrIDwtIHJlYWRPR1IoZHNuID0gcm9vdF9maWxlKCcxLWRhdGEvMy1leHRlcm5hbC93YS1ibGsvYmxvY2tzMTAvJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5ZXIgPSAnYmxvY2tzMTAnLAogICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCiAgICAgICAgCiAgICAgICAga2NfYmxrQGRhdGEgJTw+JSBsZWZ0X2pvaW4oY250cywgYnkgPSAnR0VPX0lEX0JMSycpICU+JSAKICAgICAgICAgICAgICAgIHNlbGVjdChHRU9JRF9CTEsgPSBHRU9fSURfQkxLLAogICAgICAgICAgICAgICAgICAgICAgIEdFT0lEX1RSOlBPUEhVKQogICAgICAgIAogICAgICAgIGtjX2JsayAlPiUgd3JpdGVPR1IoZHNuID0gcm9vdF9maWxlKCcxLWRhdGEvNC1pbnRlcmltL2tjLWJsay1zcC5ncGtnJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXllciA9ICdrY19ibGtfc3AnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZHJpdmVyID0gJ0dQS0cnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgb3ZlcndyaXRlX2xheWVyID0gVFJVRSx2ZXJib3NlID0gRkFMU0UpCn0KCmtjX2Jsa19zcCA8LSByZWFkT0dSKGRzbiA9IHJvb3RfZmlsZSgnMS1kYXRhLzQtaW50ZXJpbS9rYy1ibGstc3AuZ3BrZycpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5ZXIgPSAna2NfYmxrX3NwJywKICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UpCgoKCmBgYAoKCiMjIyBUaGUgQWxnb3JpdGhtIApUaGUgZm9sbG93aW5nIGFjdGlvbnMgYXJlIHBlcmZvcm1lZCBpbiB0aGlzIG1ldGhvZDoKCiAgMS4gQ2VudHJvaWRzIG9mIHRoZSBjZW5zdXMgYmxvY2sgcG9seWdvbnMgYXJlIGNhbGN1bGF0ZWQgKGBjbGFzcyA9IFNwYXRpYWxQb2ludHNEYXRhRnJhbWVgKQogIDIuIEhSQSBJRHMgYXJlIHBhc3NlZCB0byB0aGUgYmxvY2sgY2VudHJvaWQgdXNpbmcgYSBzcGF0aWFsIG92ZXJsYXkgbWV0aG9kIChgc3A6Om92ZXIoKWApCiAgMy4gQmxvY2tzIGFyZSBhZ2dyZWdhdGVkIGludG8gdHJhY3RzIGFuZCB0aGUgY291bnQgdmFyaWFibGVzIChgUE9QYCxgSFVgLGBQT1BIVWApIGFyZSBzdW1tZWQKICA0LiBGb3IgZWFjaCBjb3VudCB2YXJpYWJsZSwgdGhlIEhSQSBJRCB3aXRoIHRoZSBoaWdoZXN0IHN1bSBpcyBhc3NpZ25lZCB0byBlYWNoIHRyYWN0CiAgCmBgYHtyIGhyYS1hc3NpZ259CgojIFBhc3MgSFJBIElEcyB0byB0aGUgYmxvY2sgY2VudHJvaWRzCmhyYSA8LSByZWFkT0dSKGRzbiA9IHJvb3RfZmlsZSgnMS1kYXRhLzMtZXh0ZXJuYWwvbWFudWFsL0hSQV8yMDEwQmxvY2tfQ2xpcC8nKSxsYXllciA9ICdIUkFfMjAxMEJsb2NrX0NsaXAnLAogICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0Usc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKa2NfYmxrX2NudF9zcCA8LSBTcGF0aWFsUG9pbnRzRGF0YUZyYW1lKGNvb3JkcyA9IHJnZW9zOjpnQ2VudHJvaWQoa2NfYmxrX3NwLGJ5aWQgPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBhcy5kYXRhLmZyYW1lKGtjX2Jsa19zcEBkYXRhKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hdGNoLklEID0gRkFMU0UpCgprY19ibGtfY250X3NwJEhSQV9JRCA8LSBzcDo6b3ZlcihrY19ibGtfY250X3NwLGhyYVssJ0hSQTIwMTB2Ml8nXSkgJT4lIHVubGlzdAoKa2NfYmxrX2NudF9zcEBkYXRhICU8PiUgbXV0YXRlKEhSQV9JRCA9IGlmZWxzZShpcy5uYShIUkFfSUQpLCdOb25lJyxIUkFfSUQpKQoKCiMgQXNzaWduIEhSQXMgdG8gdHJhY3RzCiAKaWYoIWZpbGUuZXhpc3RzKHJvb3RfZmlsZSgnMS1kYXRhLzQtaW50ZXJpbS9rYy1ocmEtdHItc3AuZ3BrZycpKSl7CiAgICAgICAgaWYoIWZpbGUuZXhpc3RzKHJvb3RfZmlsZSgnMS1kYXRhLzMtZXh0ZXJuYWwva2MtdHIvdHJhY3RzMTBfc2hvcmUvdHJhY3RzMTBfc2hvcmUuc2hwJykpKXsKICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgdXJsIDwtICdmdHA6Ly9mdHAua2luZ2NvdW50eS5nb3YvZ2lzLXdlYi93ZWIvR0lTRGF0YS90cmFjdHMxMF9zaG9yZV9TSFAuemlwJyAjIGRpcmVjdCBVUkwgdG8gdGhlIGZpbGUgZG93bmxvYWQKICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgdGVtcCA8LSB0ZW1wZmlsZSgpICMgY3JlYXRlIGEgdGVtcG9yYXJ5IGZpbGUgdG8gaG9sZCB0aGUgY29tcHJlc3NlZCBkb3dubG9hZAogICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICBkb3dubG9hZCh1cmwsIGRlc3QgPSB0ZW1wLCBtb2RlPSd3YicpICMgZG93bmxvYWQgdGhlIGZpbGUKICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgdW56aXAgKHRlbXAsIGV4ZGlyID0gcm9vdF9maWxlKCcxLWRhdGEvMy1leHRlcm5hbC9rYy10cicpKSAjIGV4dHJhY3QgdGhlIGZpbGUgdG8gdGhlIHByb2plY3QgZm9sZGVyCiAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgIAogICAgICAgIH0KICAgICAgICAgCiAgICAgICAga2NfdHJfc3AgPC0gcmVhZE9HUihkc24gPSByb290X2ZpbGUoJzEtZGF0YS8zLWV4dGVybmFsL2tjLXRyL3RyYWN0czEwX3Nob3JlLycpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5ZXIgPSAndHJhY3RzMTBfc2hvcmUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQogICAgICAgIGtjX3RyX3NwQGRhdGEgJTw+JSBzZWxlY3QoR0VPSURfVFIgPSBHRU9fSURfVFJUKQogICAgICAgIAogICAgICAgIGZpcnN0X25vdE5BIDwtIGZ1bmN0aW9uKHgpe2ZpcnN0KHhbIWlzLm5hKHgpXSl9CiAgICAgICAgCiAgICAgICAgdHJfaHJhX2lkcyA8LSAKICAgICAgICAgICAgICAgIGtjX2Jsa19jbnRfc3BAZGF0YSAlPiUgCiAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lIAogICAgICAgICAgICAgICAgZ2F0aGVyKCdWQVInLCdDT1VOVCcsUE9QOlBPUEhVKSAlPiUgCiAgICAgICAgICAgICAgICBncm91cF9ieShHRU9JRF9UUixWQVIsSFJBX0lEKSAlPiUgCiAgICAgICAgICAgICAgICBzdW1tYXJpc2UoU1VNID0gc3VtKENPVU5UKSkgJT4lIAogICAgICAgICAgICAgICAgYXJyYW5nZShkZXNjKFNVTSkpICU+JSAKICAgICAgICAgICAgICAgIHNsaWNlKDEpICU+JSAKICAgICAgICAgICAgICAgIHNwcmVhZChWQVIsSFJBX0lEKSAlPiUgCiAgICAgICAgICAgICAgICBtdXRhdGUoSFVfQ05UID0gaWZlbHNlKCFpcy5uYShIVSksU1VNLE5BX2ludGVnZXJfKSwKICAgICAgICAgICAgICAgICAgICAgICBQT1BfQ05UID0gaWZlbHNlKCFpcy5uYShQT1ApLFNVTSxOQV9pbnRlZ2VyXyksCiAgICAgICAgICAgICAgICAgICAgICAgUE9QSFVfQ05UID0gaWZlbHNlKCFpcy5uYShQT1BIVSksU1VNLE5BX2ludGVnZXJfKSkgJT4lIAogICAgICAgICAgICAgICAgc2VsZWN0KC1TVU0pICU+JSAKICAgICAgICAgICAgICAgIGdyb3VwX2J5KEdFT0lEX1RSKSAlPiUgCiAgICAgICAgICAgICAgICBzdW1tYXJpc2VfYWxsKGZ1bnMoZmlyc3Rfbm90TkEpKSAlPiUgCiAgICAgICAgICAgICAgICB1bmdyb3VwKCkgJT4lIAogICAgICAgICAgICAgICAgc2VsZWN0KEdFT0lEX1RSLAogICAgICAgICAgICAgICAgICAgICAgIFBPUCA9IFBPUF9DTlQsCiAgICAgICAgICAgICAgICAgICAgICAgSFUgPSBIVV9DTlQsCiAgICAgICAgICAgICAgICAgICAgICAgUE9QSFUgPSBQT1BIVV9DTlQsCiAgICAgICAgICAgICAgICAgICAgICAgSFJBX1BPUCA9IFBPUCwKICAgICAgICAgICAgICAgICAgICAgICBIUkFfSFUgPSBIVSwKICAgICAgICAgICAgICAgICAgICAgICBIUkFfUE9QSFUgPSBQT1BIVSkKICAgICAgICAKICAgICAgICB0cl9ocmFfaWRzMiA8LSAKICAgICAgICAgICAgICAgIHRyX2hyYV9pZHMgJT4lIAogICAgICAgICAgICAgICAgc2VsZWN0KEdFT0lEX1RSLG1hdGNoZXMoJ0hSQScpKSAlPiUgCiAgICAgICAgICAgICAgICBnYXRoZXIoIlZBUiIsIkhSQSIsbWF0Y2hlcygnSFJBJykpICU+JSAgCiAgICAgICAgICAgICAgICBncm91cF9ieShHRU9JRF9UUikgJT4lIAogICAgICAgICAgICAgICAgc3VtbWFyaXNlKEFMTEVRID0gbGVuZ3RoKHVuaXF1ZShIUkEpKT09MSkgJT4lCiAgICAgICAgICAgICAgICBsZWZ0X2pvaW4odHJfaHJhX2lkcywuLGJ5ID0gJ0dFT0lEX1RSJykgCiAgICAgICAgCiAgICAgICAgIyBHZXQgdGhlIHBlcmNlbnRhZ2UgZm9yIGVhY2ggY291bnQgdmFyaWFibGUKICAgICAgICAKICAgICAgICBwb3BfdHIgPC0gCiAgICAgICAgICAgICAgICByZWFkX2Nzdihyb290X2ZpbGUoIjEtZGF0YS8zLWV4dGVybmFsL21hbnVhbC9rYy10ci9ERUNfMTBfU0YxX1AxL0RFQ18xMF9TRjFfUDFfd2l0aF9hbm4uY3N2IiksIAogICAgICAgICAgICAgICAgICAgICAgICAgY29sX3R5cGVzID0gY29scyhHRU8uaWQyID0gY29sX2NoYXJhY3RlcigpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR2VvZ3JhcGh5ID0gY29sX3NraXAoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElkID0gY29sX3NraXAoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElkMiA9IGNvbF9jaGFyYWN0ZXIoKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgc2tpcCA9IDEpICU+JSAKICAgICAgICAgICAgICAgIHNlbGVjdChHRU9JRF9UUiA9IElkMiwKICAgICAgICAgICAgICAgICAgICAgICBQT1BfVFIgPSBUb3RhbCkKICAgICAgICAKICAgICAgICBodV90ciA8LSAKICAgICAgICAgICAgICAgIHJlYWRfY3N2KHJvb3RfZmlsZSgnMS1kYXRhLzMtZXh0ZXJuYWwvbWFudWFsL2tjLXRyL0RFQ18xMF9TRjFfSDEvREVDXzEwX1NGMV9IMV93aXRoX2Fubi5jc3YnKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBjb2xfdHlwZXMgPSBjb2xzKEdFTy5pZDIgPSBjb2xfY2hhcmFjdGVyKCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHZW9ncmFwaHkgPSBjb2xfc2tpcCgpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSWQgPSBjb2xfc2tpcCgpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSWQyID0gY29sX2NoYXJhY3RlcigpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBza2lwID0gMSklPiUgCiAgICAgICAgICAgICAgICBzZWxlY3QoR0VPSURfVFIgPSBJZDIsCiAgICAgICAgICAgICAgICAgICAgICAgSFVfVFIgPSBUb3RhbCkKICAgICAgICAKICAgICAgICBwb3BodV90ciA8LSAKICAgICAgICAgICAgICAgIHJlYWRfY3N2KHJvb3RfZmlsZSgnMS1kYXRhLzMtZXh0ZXJuYWwvbWFudWFsL2tjLXRyL0RFQ18xMF9TRjFfSDEwL0RFQ18xMF9TRjFfSDEwX3dpdGhfYW5uLmNzdicpLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbF90eXBlcyA9IGNvbHMoR0VPLmlkMiA9IGNvbF9jaGFyYWN0ZXIoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdlb2dyYXBoeSA9IGNvbF9za2lwKCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJZCA9IGNvbF9za2lwKCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJZDIgPSBjb2xfY2hhcmFjdGVyKCkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHNraXAgPSAxKSAlPiUgCiAgICAgICAgICAgICAgICBzZWxlY3QoR0VPSURfVFIgPSBJZDIsCiAgICAgICAgICAgICAgICAgICAgICAgUE9QSFVfVFIgPSBUb3RhbCkKICAgICAgICAKICAgICAgICBrY190cl9zcEBkYXRhICU8PiUKICAgICAgICAgICAgICAgIGxlZnRfam9pbiguLHRyX2hyYV9pZHMyLGJ5ID0gJ0dFT0lEX1RSJykgJT4lIAogICAgICAgICAgICAgICAgbGVmdF9qb2luKC4scG9wX3RyLGJ5ID0gJ0dFT0lEX1RSJykgJT4lIAogICAgICAgICAgICAgICAgbGVmdF9qb2luKC4saHVfdHIsYnkgPSAnR0VPSURfVFInKSAlPiUgCiAgICAgICAgICAgICAgICBsZWZ0X2pvaW4oLixwb3BodV90cixieSA9ICdHRU9JRF9UUicpICU+JSAKICAgICAgICAgICAgICAgIG11dGF0ZShQT1BfUENUID0gcm91bmRfYW55KFBPUC9QT1BfVFIsYWNjdXJhY3kgPSAuMDEpLAogICAgICAgICAgICAgICAgICAgICAgIEhVX1BDVCA9IHJvdW5kX2FueShIVS9IVV9UUixhY2N1cmFjeSA9IC4wMSksCiAgICAgICAgICAgICAgICAgICAgICAgUE9QSFVfUENUID0gcm91bmRfYW55KFBPUEhVL1BPUEhVX1RSLGFjY3VyYWN5ID0gLjAxKSkgJT4lIAogICAgICAgICAgICAgICAgc2VsZWN0KEdFT0lEX1RSLAogICAgICAgICAgICAgICAgICAgICAgIFBPUCxQT1BfUENULAogICAgICAgICAgICAgICAgICAgICAgIEhVLCBIVV9QQ1QsCiAgICAgICAgICAgICAgICAgICAgICAgUE9QSFUsIFBPUEhVX1BDVCxldmVyeXRoaW5nKCkpCiAgICAgICAgCiAgICAgICAga2NfdHJfc3AgJT4lIAogICAgICAgICAgICAgICAgd3JpdGVPR1IoZHNuID0gcm9vdF9maWxlKCcxLWRhdGEvNC1pbnRlcmltL2tjLWhyYS10ci1zcC5ncGtnJyksCiAgICAgICAgICAgICAgICAgICAgICAgICBsYXllciA9ICdrY19ocmFfdHJfc3AnLAogICAgICAgICAgICAgICAgICAgICAgICAgZHJpdmVyID0gJ0dQS0cnLAogICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgb3ZlcndyaXRlX2xheWVyID0gVFJVRSkKICAgICAgICAKfQoKa2NfaHJhX3RyX3NwIDwtIAogICAgICAgIHJlYWRPR1IoZHNuID0gcm9vdF9maWxlKCcxLWRhdGEvNC1pbnRlcmltL2tjLWhyYS10ci1zcC5ncGtnJyksCiAgICAgICAgICAgICAgICBsYXllciA9ICdrY19ocmFfdHJfc3AnLAogICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFKSAlPiUgCiAgICAgICAgc3BUcmFuc2Zvcm0oY3JzX3Byb2opCgpgYGAKCkFmdGVyIHJ1bm5pbmcgdGhlIGFzc2lnbm1lbnQgYWxnb3JpdGhtLCBpdCBpcyBjbGVhciB0aGF0IHRoZSBgUE9QYCBhbmQgYFBPUEhVYCB2YXJpYWJsZXMgcmVzdWx0IGluIHRoZSBzYW1lIEhSQSBhc3NpZ25tZW50cy4gYEhVYCBkaWZmZXJzIGZyb20gdGhlIG90aGVyIHR3byB2YXJpYWJsZXMgaW4gdGhyZWUgb2YgdGhlIHRyYWN0czoKCmBgYHtyIGhyYS1hc3NpZ24tcmVzdWx0cywgZmlnLmNhcD0nRGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBtZXRob2RzJ30KCmtjX2hyYV90cl9zcEBkYXRhICU+JSAKICAgICAgICBtdXRhdGUoQUxMRVEgPSBhcy5sb2dpY2FsKEFMTEVRKSkgJT4lIAogICAgICAgIGZpbHRlcihBTExFUSA9PSBGQUxTRSkgJT4lIAogICAgICAgIHNlbGVjdChHRU9JRF9UUixIUkFfUE9QLEhSQV9QT1BIVSxIUkFfSFUpICU+JSAKICAgICAgICBrYWJsZShjYXB0aW9uID0gJ0RpZmZlcmVuY2UgYmV0d2VlbiB0aGUgbWV0aG9kcycpCgpgYGAKCgojIyMgTWFwcwoKCmBgYHtyIGhyYS1tYWtlLW1hcHN9CgpteXBhbCA8LSBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoOCxuYW1lID0gJ1NldDInKVstOF0KCnNodWZmbGVkX2hyYV9wb3AgPC0gZm9yY2F0czo6ZmN0X3NodWZmbGUoa2NfaHJhX3RyX3NwJEhSQV9QT1ApICU+JSBmYWN0b3Iob3JkZXJlZCA9IFQpCnNodWZmbGVkX2hyYV9odSA8LSBmb3JjYXRzOjpmY3Rfc2h1ZmZsZShrY19ocmFfdHJfc3AkSFJBX0hVKSAlPiUgZmFjdG9yKG9yZGVyZWQgPSBUKQpzaHVmZmxlZF9ocmFfcG9waHUgPC0gZm9yY2F0czo6ZmN0X3NodWZmbGUoa2NfaHJhX3RyX3NwJEhSQV9QT1BIVSkgJT4lIGZhY3RvcihvcmRlcmVkID0gVCkKCnBhbF9wb3AgPC0gY29sb3JGYWN0b3IocGFsZXR0ZSA9IG15cGFsLGRvbWFpbiA9IHNodWZmbGVkX2hyYV9wb3ApCnBhbF9odSA8LSBjb2xvckZhY3RvcihwYWxldHRlID0gbXlwYWwsZG9tYWluID0gc2h1ZmZsZWRfaHJhX2h1KQpwYWxfcG9waHUgPC0gY29sb3JGYWN0b3IocGFsZXR0ZSA9IG15cGFsLGRvbWFpbiA9IHNodWZmbGVkX2hyYV9wb3BodSkKCmhyYSAlPD4lIHNwVHJhbnNmb3JtKGNyc19wcm9qKQoKc2hvd19ocmFfdHJfcG9wIDwtIGZ1bmN0aW9uKCl7CiAgICAgICAgbXlMZmx0R3JleShidW1wTGFiZWxzID0gRkFMU0UsaGlkZUNvbnRyb2xzID0gRkFMU0UpICU+JQogICAgICAgIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJENhcnRvREIpICU+JSAKICAgICAgICBhZGRQb2x5Z29ucyhkYXRhID0ga2NfaHJhX3RyX3NwLAogICAgICAgICAgICAgICAgICAgIHNtb290aEZhY3RvciA9IDAsCiAgICAgICAgICAgICAgICAgICAgd2VpZ2h0ID0gMSwKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGNvbDJoZXgoIndoaXRlIiksCiAgICAgICAgICAgICAgICAgICAgb3BhY2l0eSA9IC44NSwKICAgICAgICAgICAgICAgICAgICBmaWxsQ29sb3IgPSB+cGFsX3BvcChzaHVmZmxlZF9ocmFfcG9wKSwKICAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IC41LAogICAgICAgICAgICAgICAgICAgIGdyb3VwID0gJ0hSQSBUcmFjdCBieSBQb3AuJywKICAgICAgICAgICAgICAgICAgICBwb3B1cCA9IH5wYXN0ZTAoa2NfaHJhX3RyX3NwJEdFT0lEX1RSKSkgJT4lIAogICAgICAgIGFkZFBvbHlnb25zKGRhdGEgPSBocmEsCiAgICAgICAgICAgICAgICAgICAgc21vb3RoRmFjdG9yID0gMCwKICAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAsCiAgICAgICAgICAgICAgICAgICAgd2VpZ2h0ID0gMiwKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IH5wYWxfcG9wKGZhY3RvcihocmEkSFJBMjAxMHYyXyxsZXZlbHMgPSBsZXZlbHMoc2h1ZmZsZWRfaHJhX3BvcCksb3JkZXJlZCA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICAgICBvcGFjaXR5ID0gMSwKICAgICAgICAgICAgICAgICAgICBncm91cCA9ICdIUkFzJywKICAgICAgICAgICAgICAgICAgICBwb3B1cCA9IH5wYXN0ZTAoaHJhJEhSQTIwMTB2Ml8pKSAlPiUgCiAgICAgICAgYWRkTGF5ZXJzQ29udHJvbChvdmVybGF5R3JvdXBzID0gYygnSFJBIFRyYWN0IGJ5IFBvcC4nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0hSQXMnKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gJ3RvcHJpZ2h0JyxvcHRpb25zID0gbGF5ZXJzQ29udHJvbE9wdGlvbnMoRkFMU0UpKQp9CgpzaG93X2hyYV90cl9odSA8LSBmdW5jdGlvbigpewogICAgICAgIG15TGZsdEdyZXkoYnVtcExhYmVscyA9IEZBTFNFLGhpZGVDb250cm9scyA9IEZBTFNFKSAlPiUKICAgICAgICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRDYXJ0b0RCKSAlPiUgCiAgICAgICAgYWRkUG9seWdvbnMoZGF0YSA9IGtjX2hyYV90cl9zcCwKICAgICAgICAgICAgICAgICAgICBzbW9vdGhGYWN0b3IgPSAwLAogICAgICAgICAgICAgICAgICAgIHdlaWdodCA9IDEsCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb2wyaGV4KCJ3aGl0ZSIpLAogICAgICAgICAgICAgICAgICAgIG9wYWNpdHkgPSAuODUsCiAgICAgICAgICAgICAgICAgICAgZmlsbENvbG9yID0gfnBhbF9odShzaHVmZmxlZF9ocmFfaHUpLAogICAgICAgICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gLjUsCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSAnSFJBIFRyYWN0IGJ5IEhVLicsCiAgICAgICAgICAgICAgICAgICAgcG9wdXAgPSB+cGFzdGUwKGtjX2hyYV90cl9zcCRHRU9JRF9UUikpICU+JSAKICAgICAgICBhZGRQb2x5Z29ucyhkYXRhID0gaHJhLAogICAgICAgICAgICAgICAgICAgIHNtb290aEZhY3RvciA9IDAsCiAgICAgICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLAogICAgICAgICAgICAgICAgICAgIHdlaWdodCA9IDIsCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+cGFsX2h1KGZhY3RvcihocmEkSFJBMjAxMHYyXyxsZXZlbHMgPSBsZXZlbHMoc2h1ZmZsZWRfaHJhX2h1KSxvcmRlcmVkID0gVFJVRSkpLAogICAgICAgICAgICAgICAgICAgIG9wYWNpdHkgPSAxLAogICAgICAgICAgICAgICAgICAgIGdyb3VwID0gJ0hSQXMnLAogICAgICAgICAgICAgICAgICAgIHBvcHVwID0gfnBhc3RlMChocmEkSFJBMjAxMHYyXykpICU+JSAKICAgICAgICBhZGRMYXllcnNDb250cm9sKG92ZXJsYXlHcm91cHMgPSBjKCdIUkEgVHJhY3QgYnkgSFUuJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdIUkFzJyksCiAgICAgICAgICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9ICd0b3ByaWdodCcsb3B0aW9ucyA9IGxheWVyc0NvbnRyb2xPcHRpb25zKEZBTFNFKSkKfQoKc2hvd19ocmFfdHJfcG9waHUgPC0gZnVuY3Rpb24oKXsKICAgICAgICBteUxmbHRHcmV5KGJ1bXBMYWJlbHMgPSBGQUxTRSxoaWRlQ29udHJvbHMgPSBGQUxTRSkgJT4lCiAgICAgICAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkQ2FydG9EQikgJT4lIAogICAgICAgIGFkZFBvbHlnb25zKGRhdGEgPSBrY19ocmFfdHJfc3AsCiAgICAgICAgICAgICAgICAgICAgc21vb3RoRmFjdG9yID0gMCwKICAgICAgICAgICAgICAgICAgICB3ZWlnaHQgPSAxLAogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY29sMmhleCgid2hpdGUiKSwKICAgICAgICAgICAgICAgICAgICBvcGFjaXR5ID0gLjg1LAogICAgICAgICAgICAgICAgICAgIGZpbGxDb2xvciA9IH5wYWxfcG9waHUoc2h1ZmZsZWRfaHJhX3BvcGh1KSwKICAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IC41LAogICAgICAgICAgICAgICAgICAgIGdyb3VwID0gJ0hSQSBUcmFjdCBieSBQb3AuIGluIEhVJywKICAgICAgICAgICAgICAgICAgICBwb3B1cCA9IH5wYXN0ZTAoa2NfaHJhX3RyX3NwJEdFT0lEX1RSKSkgJT4lIAogICAgICAgIGFkZFBvbHlnb25zKGRhdGEgPSBocmEsCiAgICAgICAgICAgICAgICAgICAgc21vb3RoRmFjdG9yID0gMCwKICAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAsCiAgICAgICAgICAgICAgICAgICAgd2VpZ2h0ID0gMiwKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IH5wYWxfcG9waHUoZmFjdG9yKGhyYSRIUkEyMDEwdjJfLGxldmVscyA9IGxldmVscyhzaHVmZmxlZF9ocmFfcG9waHUpLG9yZGVyZWQgPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgICAgb3BhY2l0eSA9IDEsCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSAnSFJBcycsCiAgICAgICAgICAgICAgICAgICAgcG9wdXAgPSB+cGFzdGUwKGhyYSRIUkEyMDEwdjJfKSkgJT4lIAogICAgICAgIGFkZExheWVyc0NvbnRyb2wob3ZlcmxheUdyb3VwcyA9IGMoJ0hSQSBUcmFjdCBieSBQb3AuIGluIEhVJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdIUkFzJyksCiAgICAgICAgICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9ICd0b3ByaWdodCcsb3B0aW9ucyA9IGxheWVyc0NvbnRyb2xPcHRpb25zKEZBTFNFKSkKfQoKIyBTYXZlIHRoZSBtYXBzIGFzIEhUTUwgZG9jdW1lbnRzCgppZighZmlsZS5leGlzdHMocm9vdF9maWxlKCczLWNvbW11bmljYXRpb24vb3RoZXJzL2h0bWwvaHJhLXRyYWN0cy1wb3AuaHRtbCcpKSl7CiAgICAgICAgc2hvd19ocmFfdHJfcG9wKCkgJT4lIAogICAgICAgIHNhdmVXaWRnZXQoZmlsZSA9IHJvb3RfZmlsZSgnMy1jb21tdW5pY2F0aW9uL290aGVycy9odG1sL2hyYS10cmFjdHMtcG9wLmh0bWwnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZmNvbnRhaW5lZCA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsaWJkaXIgPSByb290X2ZpbGUoJzMtY29tbXVuaWNhdGlvbi9vdGhlcnMvaHRtbC9odG1sX3N1cHBvcnRfZmlsZXMnKSkKfQoKaWYoIWZpbGUuZXhpc3RzKHJvb3RfZmlsZSgnMy1jb21tdW5pY2F0aW9uL290aGVycy9odG1sL2hyYS10cmFjdHMtaHUuaHRtbCcpKSl7CiAgICAgICAgc2hvd19ocmFfdHJfcG9wKCkgJT4lIAogICAgICAgIHNhdmVXaWRnZXQoZmlsZSA9IHJvb3RfZmlsZSgnMy1jb21tdW5pY2F0aW9uL290aGVycy9odG1sL2hyYS10cmFjdHMtaHUuaHRtbCcpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxmY29udGFpbmVkID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpYmRpciA9IHJvb3RfZmlsZSgnMy1jb21tdW5pY2F0aW9uL290aGVycy9odG1sL2h0bWxfc3VwcG9ydF9maWxlcycpKQp9CgppZighZmlsZS5leGlzdHMocm9vdF9maWxlKCczLWNvbW11bmljYXRpb24vb3RoZXJzL2h0bWwvaHJhLXRyYWN0cy1wb3BodS5odG1sJykpKXsKICAgICAgICBzaG93X2hyYV90cl9wb3AoKSAlPiUgCiAgICAgICAgc2F2ZVdpZGdldChmaWxlID0gcm9vdF9maWxlKCczLWNvbW11bmljYXRpb24vb3RoZXJzL2h0bWwvaHJhLXRyYWN0cy1wb3BodS5odG1sJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGZjb250YWluZWQgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGliZGlyID0gcm9vdF9maWxlKCczLWNvbW11bmljYXRpb24vb3RoZXJzL2h0bWwvaHRtbF9zdXBwb3J0X2ZpbGVzJykpCn0KCgpgYGAKCgpgYGB7ciBocmEtc2hvdy1wb3AsIGZpZy5jYXA9J0hSQSBDZW5zdXMgVHJhY3RzIChieSBQb3B1bGF0aW9uKSd9CnNob3dfaHJhX3RyX3BvcCgpCgpgYGAKCmBgYHtyIGhyYS1zaG93LWh1LCBmaWcuY2FwPSdIUkEgQ2Vuc3VzIFRyYWN0cyAoYnkgSG91c2luZyBVbml0cyknfQpzaG93X2hyYV90cl9odSgpCgpgYGAKCmBgYHtyIGhyYS1zaG93LXBvcGh1LCBmaWcuY2FwPSdIUkEgQ2Vuc3VzIFRyYWN0cyAoYnkgUG9wdWxhdGlvbiBpbiBIb3VzaW5nIFVuaXRzKSd9CnNob3dfaHJhX3RyX3BvcGh1KCkKCmBgYAoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCg==